# Table of Contents

# @EnableXXX 어노테이션

Spring Legacy는 프로젝트를 구성할 때 관련된 빈을 일일이 설정해야한다. 이러한 번거로움을 해소하기 위해 스프링은 사전에 합리적으로 정의된 설정을 자동으로 적용하는 기능을 제공하기 시작했는데 그것이 바로 @Enable로 시작하는 어노테이션이다.

  • @EnableWebMvc
  • @EnableJpaRepositories
  • @EnableTransactionManagement
  • @EnableJpaAuditing
  • @EnableWebSecurity
  • @EnableAsync
  • @EnableWebSocket
  • @EnableCaching
  • @EnableScheduling

@EnableXXX 어노테이션을 @Configuration이 붙은 구성 클래스에 붙이면 사전에 합리적으로 정의된 설정에 따라 필요한 빈들이 자동으로 등록된다. @EnableWebMvc의 경우 WebMvcConfigurationSupport클래스를 사용하여 필요한 빈들을 자동으로 등록한다.

@Configuration
@EnableWebMvc
public class WebMvcConfig {
    // ...
}

물론 @EnableXXX 어노테이션을 통한 설정을 커스터마이징할 수도 있다. 이 때는 XXXConfigurer로 끝나는 인터페이스를 구현하면 된다.

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
      @Override
      public void addFormatters(FormatterRegistry formatterRegistry) {
          formatterRegistry.addConverter(new MyConverter());
      }
 
      @Override
      public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
          converters.add(new MyHttpMessageConverter());
      }
}

참고로 인터페이스가 default 메소드를 제공하지 않던 시절에는 인터페이스인 XXXConfigurer 인터페이스를 구현하기 위해 모든 메소드를 오버라이드 해야했다. 이러한 불편함을 해결하기 위해 XXXConfigurerAdapter 클래스를 제공하여 필요한 메소드만 구현하도록 했다.

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurerAdapter {
    // 필요한 메소드만 구현
}

그러나 Java 8부터 default 메소드가 등장하면서 XXXConfigurerAdapter 클래스는 Deprecated 되었다.

public interface WebMvcConfigurer {

	default void configurePathMatch(PathMatchConfigurer configurer) {
	}

	default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
	}

	default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
	}

	default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
	}

	default void addFormatters(FormatterRegistry registry) {
	}

	default void addInterceptors(InterceptorRegistry registry) {
	}

	default void addResourceHandlers(ResourceHandlerRegistry registry) {
	}

	default void addCorsMappings(CorsRegistry registry) {
	}

	default void addViewControllers(ViewControllerRegistry registry) {
	}

	default void configureViewResolvers(ViewResolverRegistry registry) {
	}

	default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
	}

	default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
	}

	default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
	}

	default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
	}

	default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
	}

	default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
	}

	@Nullable
	default Validator getValidator() {
		return null;
	}

	@Nullable
	default MessageCodesResolver getMessageCodesResolver() {
		return null;
	}
}

@EnableXXXX 어노테이션의 편리함에도 불구하고 이 어노테이션을 매번 붙여주는 것도 번거롭기 때문에 이러한 설정까지 자동화해주는 Spring Boot 모듈을 만들게 된다.

# Spring Boot와 Auto Configuration

Spring Boot는 Auto Configuration 기능으로 프로젝트에 추가한 의존성을 바탕으로 필요한 설정들을 자동으로 구성해준다. 이 덕분에 Spring Boot는 Spring Legacy에 비해 설정이 매우 쉽다.

Auto Configurationorg.springframework.boot:spring-boot-autoconfiguration 라이브러리에 포함되어있다.

# @EnableAutoConfiguration

Auto Configuration 기능은 @EnableAutoConfiguration 어노테이션을 추가하면 활성화된다.

 





@EnableAutoConfiguration
@Configuration
public class SpringBootConfig {
    // ...
}

스프링부트 프로젝트는 기본적으로 @SpringBootApplication 어노테이션이 붙어있다.

 






@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

이 어노테이션은 내부적으로 @EnableAutoConfiguration을 사용한다. 따라서 별도의 설정이 없다면 Auto Configuration 기능이 자동으로 활성화된다.


 








@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { 
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }
)
public @interface SpringBootApplication {
    // ...
}

그렇다면 Auto Configuration에 의해 자동으로 적용되는 실제 설정 값들은 어디에 정의되어있을까?

# spring.factories

org.springframework.boot:spring-boot-autoconfiguration.jarspring.factories에는 Auto Configuration 기능이 활성화될 때 적용할 구성클래스가 XXXAutoConfiguration 형태로 정의되어있다.

## spring.factories

# 중략...

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration
# 중략...

스프링부트는 Auto Configuration이 활성화되어있다면 이 파일에 정의된 구성 클래스를 활성화하여 필요한 빈들을 자동으로 등록한다.

예를 들어 스프링 시큐리티와 관련된 Auto Configuration 파일은 다음과 같다.



 


## spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration=,\
## 중략 .. 




 














@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ 
    SpringBootWebSecurityConfiguration.class,
    WebSecurityEnablerConfiguration.class,
    SecurityDataConfiguration.class,
    ErrorPageSecurityFilterConfiguration.class
})
public class SecurityAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
	public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
		return new DefaultAuthenticationEventPublisher(publisher);
	}

}

이 구성클래스는 내부적으로 SpringBootWebSecurityConfiguration 구성 클래스를 임포트한다.








 
 
 
 


@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {

	@Bean
	@Order(SecurityProperties.BASIC_AUTH_ORDER)
	SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
		http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
		return http.build();
	}
}

이 덕분에 스프링 시큐리티 모듈을 추가하기만 하면 별도의 설정을 하지 않아도 모든 요청에 대해 인증을 요구하는 것이다.

# spring-configuration-metadata.json

spring-configuration-metadata.json에는 자동 구성 클래스들이 사용하는 속성 기본 값들이 정의되어있다.

예를 들어 애플리케이션 서버의 포트로 사용할 속성값은 다음과 같이 정의되어있다.

{
    {
      "name": "server.port",
      "type": "java.lang.Integer",
      "description": "Server HTTP port.",
      "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties",
      "defaultValue": 8080
    }    
}

이 때문에 별도의 포트 설정을 하지 않아도 어플리케이션이 8080포트에서 실행된다. 물론 application.properties 또는 application.yml 파일에서 이를 오버라이딩할 수 있다.

## application.properties
server.port=20000

# Auto Configuration 커스터마이징 시 주의사항

이처럼 스프링 부트는 Auto Configuration 기능을 통해 필요한 빈을 자동으로 등록한다. 스프링 부트 프로젝트에서는 @EnableXXX 어노테이션을 주의해서 사용해야한다.

Spring WebMVC를 예제로 살펴보자. 스프링 부트는 WebMvcAutoConfiguration를 사용하여 Spring WebMVC와 관련된 컴포넌트를 자동으로 등록한다.

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
// 생략 ..
public class WebMvcAutoConfiguration {
	// 생략 ..
}

여기서 주목할 부분은 @ConditionalOnMissingBean 어노테이션이다. 이 어노테이션은 프로젝트에 WebMvcConfigurationSupport클래스 또는 이 클래스를 상속하는 자식 클래스가 없는 경우에만 WebMvcAutoConfiguration 구성 클래스를 활성화한다.

이제 @EnableWebMvc 어노테이션을 살펴보자.

@EnableWebMvc은 기본적으로 WebMvcConfigurationSupport클래스를 사용하여 컴포넌트를 초기화한다. 따라서 구성 클래스에 @EnableWebMvc을 붙이면 스프링 부트의 Auto ConfigurationWebMvcAutoConfiguration에 정의된 컴포넌트들이 빈에 등록되지 않게된다.

따라서 Auto Configuration에 정의된 컴포넌트를 베이스로 사용하고 이를 커스터마이징하려면 다음과 같이 @EnableWebMvc 어노테이션을 빼야한다.

// @EnableWebMvc
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
	// 생략 ...
}

WebMvcAutoConfigurationWebMvcConfigurationSupport보다 더 많은 컴포넌트를 자동으로 구성해준다. 따라서 스프링 부트 프로젝트에서 Spring WebMVC 설정을 커스터마이징 할 때는 @EnableWebMvc를 제외하여 Auto Configuration 기능을 사용하는 것이 좋다.

반면 스프링 시큐리티의 경우 스프링 부트 Auto Configuration가 제공하는 설정을 사용하지 않고 개발자가 인증과 접근 제어를 직접 설정하는 경우가 많다. 따라서 보통 다음과 같이 구성 클래스를 구현한다.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	// ..
}

스프링 부트 프로젝트에서 구성 클래스를 커스터마이징할 때는 항상 두 가지를 주의하자.

  • 스프링 부트 Auto Configuration이 자동으로 등록하는 컴포넌트와 @EnableXXX 어노테이션이 등록하는 컴포넌트가 다를 수 있다.
  • @EnableXXX 어노테이션 설정이 스프링 부트 Auto Configuration 설정보다 우선순위가 높다.